<template>
	<div class="base-form-container">
		<el-form :model="returnModel" :rules="rules" :label-width="option.labelWidth" :inline="option.inline"
				 :label-position="option.labelAlign" ref="form">
			<template v-for="(item,index) in option.formItem">
				<!--前插槽-->
				<slot :name="'form-slot-before-'+item.prop"></slot>
				<el-form-item :label="item.label"
							  :prop="item.prop"
							  :required="item.required"
							  :key="item.prop">

					<!--输入框类型-->
					<el-input v-model="returnModel[item.prop]" :placeholder="item.placeholder"
							  v-if="item.type===FormTypeEnum.text||item.type===FormTypeEnum.password||item.type===FormTypeEnum.textarea"
							  :disabled="item.disabled"
							  :readonly="item.readonly"
							  :type="item.type"
							  :rows="item.rows"/>

					<!--下拉选项-->
					<el-select v-model="returnModel[item.prop]" :placeholder="item.placeholder" ref="elSelect"
							   v-if="item.type===FormTypeEnum.selector"
							   :disabled="item.disabled"
							   :readonly="item.readonly"
							   :multiple="item.multi">
						<el-option v-for="child in item.itemList" :label="child.label" :value="child" :key="child.value"
								   :disabled="child.state"/>
					</el-select>

					<!--单选radio-->
					<el-radio-group v-model="returnModel[item.prop]"
									v-if="item.type===FormTypeEnum.radio"
									:disabled="item.disabled"
									:readonly="item.readonly">
						<el-radio v-for="child in item.itemList" :label="child.value||child.tid" :value="child"
								  :key="child.value"
								  :disabled="child.state">
							{{ child.label }}
						</el-radio>
					</el-radio-group>

					<!--多选checkbox-->
					<el-checkbox-group v-model="returnModel[item.prop]" v-if="item.type===FormTypeEnum.checkbox">
						<el-checkbox v-for="child in item.itemList" :label="child" :value="child" :key="child.value"
									 :disabled="child.state" :checked="checkboxChecked(item,child)">
							{{ child.label }}
						</el-checkbox>
					</el-checkbox-group>

					<!--switch开关-->
					<el-switch v-model="returnModel[item.prop]" v-if="item.type===FormTypeEnum.switch"
							   :disabled="item.disabled"
							   :readonly="item.readonly"/>

					<!--日期时间选择-->
					<el-date-picker v-model="returnModel[item.prop]" v-if="item.type===FormTypeEnum.datePick"
									:placeholder="item.placeholder"
									type="date"
									:format="item.format"
									:value-format="item.valueFormat"
									:editable="false"/>

					<!--日期时间选择-->
					<el-date-picker v-model="returnModel[item.prop]" v-if="item.type===FormTypeEnum.dateTimePick"
									:placeholder="item.placeholder"
									type="datetime"
									:format="item.format"
									:value-format="item.valueFormat"
									:editable="false"/>

					<!--日期时间选择-->
					<el-time-select v-model="returnModel[item.prop]" v-if="item.type === FormTypeEnum.timePick"
									:placeholder="item.placeholder"
									:picker-options="item.timeOption"
									:editable="false"/>
					<!--文件上传（图片和文件）-->
					<el-upload v-if="item.type === FormTypeEnum.file||item.type === FormTypeEnum.fileImage"
							   :class="{'upload-img': item.type === FormTypeEnum.fileImage,
							   			'upload-file': item.type === FormTypeEnum.file,
							   			'readonly': !item.multi&&fileObject[index].length>0}"
							   action=" "
							   :list-type="item.type === FormTypeEnum.fileImage ? 'picture-card': 'text'"
							   :accept="item.accept"
							   :multiple="item.multi"
							   :auto-upload="false"
							   :on-change="(file,fileList)=>fileChang(file,fileList,index,item.maxSize)"
							   :on-remove="(file,fileList)=>fileRemove(file,fileList,index)"
							   :file-list="fileObject[index]">
						<!--:on-preview="(file)=>filePreview(file,index)"-->
						<i class="el-icon-upload" v-if="item.type===FormTypeEnum.fileImage" style="font-size:42px"
						   width="90">
							<div style="font-size:14px">{{ item.placeholder }}</div>
						</i>
						<el-button class="file-upload-button" type="primary" v-else>{{ item.placeholder }}</el-button>
					</el-upload>
				</el-form-item>
				<!--后插槽-->
				<slot :name="'form-slot-after-'+item.prop"></slot>
			</template>
		</el-form>
		<!--确定、取消按钮-->
		<div class="footer-btn" v-if="showButton!==false">
			<el-button type="primary" @click="save">{{ translate('button.save') }}</el-button>
			<el-button type="primary" plain @click="submit">{{ translate('button.submit') }}</el-button>
			<el-button @click="cancel">{{ translate('button.cancel') }}</el-button>
		</div>

		<!--图片预览-->
		<!--<el-image-viewer v-if="showImageViewer" :on-close="closeImageViewer" :url-list="imageViewerList"
						 :initial-index="imageViewerIndex"/>-->
	</div>
</template>
<script lang="ts">
import _ from 'lodash';
import {Component, Emit, Prop} from "vue-property-decorator";
import BaseVue from '@/common/base-vue'

import {ElForm} from "element-ui/types/form";
import CommonApi from "@/common/api/common-api";
// import ElImageViewer from 'element-ui/packages/image/src/image-viewer'
import FileObjectModel from "./model/file-object.model";
import {FormEventEnum, FormTypeEnum} from "./enums/base-form.enum";
import BaseFormOptionModel from "./model/base-form-option.model";
import {StoreDictEnum} from "@/store/enum/store.enum";
import FormItemModel from "./model/form-item.model";
import {ElUploadInternalFileDetail} from "element-ui/types/upload";

@Component({components: {}})
export default class BaseForm extends BaseVue {
	/*文件路径前缀*/
	private urlPrefix: string;
	$refs: {
		form: ElForm
	}
	/*传入表单的配置设置*/
	@Prop(Object)
	private option: BaseFormOptionModel;

	/*是否需要显示按钮*/
	@Prop({default: false})
	private showButton;

	/*表单类型枚举*/
	private FormTypeEnum = FormTypeEnum;

	/*返回的模型（动态生成的模型）*/
	private returnModel: object = {};

	/*存储原值，用于各种计算*/
	private oldData: object = {};

	/*校验*/
	private rules = {}

	/*图片预览*/
	private showImageViewer: boolean = false;
	private imageViewerList = [];
	private imageViewerIndex: number = 0;

	/*临时使用的file，多个集合（动态生成的）*/
	private fileObject: object = {}

	/*可能会取消的请求（文件上传、动态生成的）*/
	private cancelRequest: object = {}

	/*回显勾选checkbox*/
	get checkboxChecked() {
		return (item, child) => {
			let checked = false;
			if (this.oldData[item.prop]) {
				this.oldData[item.prop].forEach(val => {
					if (child.value === val.value || child.tid === val.tid) {
						checked = true
					}
					;
				})
			}
			return checked
		}
	}

	/**
	 * 生命周期created
	 */
	private created() {
		this.urlPrefix = this.$store.getters[StoreDictEnum.urlPrefix]
		// 动态设置model的键值
		let newModelObj: object = {}
		this.option.formItem.forEach((item, index) => {
			// 如果有默认值则设置默认值
			if (item.type === FormTypeEnum.selector && item.multi) {
				newModelObj[item.prop] = item.value || [];
			} else if (item.type === FormTypeEnum.switch) {
				newModelObj[item.prop] = item.value || false;
			} else if (item.type === FormTypeEnum.file || item.type === FormTypeEnum.fileImage) {
				/*如果是图片、文件上传类型*/
				//向动态模型中添加属性，设置一个index，便于最后的output时查找文件数据所在的对象
				newModelObj[item.prop] = {file: null, index};
				//设置文件临时用的对象
				this.fileObject[index] = [];
				// 设置文件类型的默认值
				this.setFileObj(newModelObj, item, index);
			} else if (item.type === FormTypeEnum.radio) {
				// 如果是radio，拷贝一份传入的原选项列表，并设置radio标识，用于计算返回数据
				this.oldData[item.prop + 'radio'] = item.itemList;
				newModelObj[item.prop] = item.value.value || item.value.tid || ''
			} else if (item.type === FormTypeEnum.checkbox) {
				// 如果是checkbox，设置空数组，通过计算属性设置勾选状态（更改勾选状态会立即更改数据，需拷贝一份传入的原值，用于计算属性遍历）
				this.oldData[item.prop] = item.value;
				newModelObj[item.prop] = [];
			} else {
				//其他默认类型
				newModelObj[item.prop] = item.value || '';
			}

			// 设置校验规则
			if (item.type !== FormTypeEnum.switch) {
				this.rules[item.prop] = [{required: item.required, message: item.placeholder || '', trigger: 'change'}];
			}
			if (item.type !== FormTypeEnum.switch && item.validator) {
				this.rules[item.prop].push({validator: item.validator, trigger: 'change'});
			}
		})
		this.returnModel = _.cloneDeep(newModelObj);
	};

	/**
	 *生命周期mounted
	 */
	private mounted() {
	};

	/**
	 * 设置图片、文件上传类型的默认数据
	 */
	private setFileObj(newModelObj: object, item: FormItemModel, index: number) {
		if (item.multi) {
			newModelObj[item.prop].file = [];
		} else {
			newModelObj[item.prop].file = {};
		}
		//如果是多选并且有数据则设置数组
		if (_.isArray(item.value) && !_.isEmpty(item.value)) {
			item.value.forEach(file => {
				this.newFileObj(file, index)
			})
		}
		//如果为单文件，构造文件对象
		if (!_.isArray(item.value) && _.isObject(item.value) && !_.isEmpty(item.value)) {
			let file = item.value as FileObjectModel;
			this.newFileObj(file, index)
		}
	}

	/**
	 * 构造文件对象，并push到列表
	 */
	private newFileObj(file: FileObjectModel, index: number) {
		let obj = {
			name: file.fileName,
			url: this.urlPrefix + file.filePath,

			fileId: file.fileId,
			filePath: file.filePath,
			fileName: file.fileName
		};
		this.fileObject[index].push(obj);
	}

	/**
	 * 保存事件返回数据
	 */
	@Emit(FormEventEnum.save)
	private save(): object {
		return this.getData()
	}

	/**
	 *提交事件返回数据
	 */
	@Emit(FormEventEnum.submit)
	private submit(): object {
		return this.getData()
	}

	/**
	 * 取消事件
	 */
	@Emit(FormEventEnum.cancel)
	private cancel() {
	}

	/**
	 * 提供外部调用获取数据方法
	 */
	public getData(): object {
		let fromData = null;
		this.$refs.form.validate((valid: boolean) => {
			if (valid && this.validateUpload()) {
				this.filterFile();//查找返回文件
				fromData = _.cloneDeep(this.returnModel);

				// 查找radio并重新赋值
				Object.keys(this.returnModel).forEach(item => {
					if (this.oldData[item + 'radio']) {
						this.oldData[item + 'radio'].forEach(val => {
							if (val.value === this.returnModel[item] || val.tid === this.returnModel[item]) {
								fromData[item] = val
							}
						})
					}
				})
			}
		})
		return fromData;
	}

	/**
	 * 验证文件上传
	 */
	private validateUpload(): boolean {
		let item = this.option.formItem;
		for (let i = 0; i < item.length; i++) {
			if (item[i].required && (item[i].type === FormTypeEnum.file || item[i].type === FormTypeEnum.fileImage) &&
					this.fileObject[i].length === 0) {
				this.$message.warning(this.translate('tips.pleaseUpload') + item[i].label)
				return false
			}
			if (item[i].required && (item[i].type === FormTypeEnum.file || item[i].type === FormTypeEnum.fileImage) &&
					this.fileObject[i].find(item => item.status === 'uploading')) {
				this.$message.warning(this.translate('tips.pleaseWaite') + item[i].label + this.translate('tips.uploadComplete'))
				return false
			}
		}
		return true;
	}

	/**
	 * 查找并返回上传文件的数据
	 */
	private filterFile() {
		for (let i in this.returnModel) {
			// 如果有file说明是文件数据对象
			if (this.returnModel[i].file) {
				//index已在初始化中临时存储，所以可以直接取文件对象中的下标进行循环处理
				const index = this.returnModel[i].index;
				// 如果是多文件数组
				if (_.isArray(this.returnModel[i].file)) {
					this.returnModel[i] = [];
					this.fileObject[index].forEach(item => {
						if (item.status === 'success') {
							// 去掉多余的字段
							this.returnModel[i].push(this.outFile(item));
						}
					});
				}
				// 如果是单文件对象
				if (!_.isArray(this.returnModel[i].file) && _.isObject(this.returnModel[i].file)) {
					this.fileObject[index].forEach(item => {
						if (item.status === 'success') {
							// 去掉多余的字段
							this.returnModel[i] = this.outFile(item);
						}
					});
				}
			}
		}
	}

	/**
	 * 去掉文件对象多余的字段，保持输入输出一致
	 */
	private outFile(file: FileObjectModel): FileObjectModel {
		return {fileName: file.fileName, fileId: file.fileId, filePath: file.filePath}
	}

	/**
	 * 查看大图
	 */
	private filePreview(file: FileObjectModel, objIndex: number) {
		this.showImageViewer = true;
		this.imageViewerList = this.fileObject[objIndex].map((item, index) => {
			if (item.fileId === file.fileId) this.imageViewerIndex = index;
			return item.url
		});
	}

	/**
	 * 关闭查看大图
	 */
	private closeImageViewer() {
		this.showImageViewer = false;
	}

	/**
	 * 删除文件、图片
	 */
	private fileRemove(file: ElUploadInternalFileDetail, fileList: ElUploadInternalFileDetail[], objIndex: number) {
		this.$set(this.fileObject, objIndex, fileList);
		//强制更新
		this.$forceUpdate();
		//取消上传请求
		this.cancelRequest[objIndex] && this.cancelRequest[objIndex]();
	}

	/**
	 * 文件选择并上传
	 */
	private fileChang(file: ElUploadInternalFileDetail, fileList: ElUploadInternalFileDetail[], objIndex: number, maxSize: number) {
		//检查文件大小
		if (!this.checkSize(file, maxSize)) {
			_.remove(fileList, file);
			return;
		}

		// 创建FormData提交文件
		let fileData = new FormData();
		fileData.append('file', file.raw);
		file.status = 'uploading';
		this.fileObject[objIndex] = fileList;
		this.$forceUpdate();//强制更新

		// 提交接口
		CommonApi.upload(fileData,
				//上传进度回调
				(progress) => {
					file.percentage = progress;
				},
				//设置取消请求的方法
				(cancel) => {
					this.cancelRequest[objIndex] = cancel;
				}
		).then(res => {
			if (res.status === 200) {
				file.status = 'success';

				file['fileId'] = res.data.tid;
				file['filePath'] = res.data.fullPath;
				file['fileName'] = res.data.name;
			} else {
				_.remove(fileList, file);
			}
		}).catch(() => {
			_.remove(fileList, file);
		}).finally(() => {
			this.fileObject[objIndex] = _.cloneDeep(fileList);
			this.$forceUpdate();//强制更新
			this.cancelRequest[objIndex] = null;//把取消请求的方法置空
		})
	};

	/**
	 * 检查文件大小限制
	 */
	private checkSize(file: ElUploadInternalFileDetail, maxSize: number): boolean {
		if (maxSize && file.size / 1024 > maxSize) {
			this.$message.warning(this.translate('tips.fileMaxSize') + maxSize + 'kb');
			return false
		} else {
			return true
		}
	}
};
</script>
<style lang="scss" scoped>
.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after {
	width: 12px;
	height: 12px;
	line-height: 1;
	background-color: $base-color-blue;
	color: $base-color-white;
	transform: translateY(10px);
	font-weight: normal;
}

.el-select-dropdown__item {
	font-weight: normal;
}

</style>
